package com.ubxtech.core.hashring;

import com.ubxtech.core.utils.Md5Util;
import com.ubxtech.core.utils.UuidUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

/**
 *
 * 一致性hash算法,处理服务节点上下线问题
 * https://blog.csdn.net/u011305680/article/details/79721030
 * @author Victor.Xiao
 * @since 2023-12-09 16:50
*/
@Slf4j
public class ConsistentHashingVirtualNode {
    //虚拟节点的数目
    private static final Integer VIRTUAL_NODES = 5;
    /** 哈希环本体 K: 虚拟节点 key 的哈希， V: 虚拟节点 */
    private final SortedMap<Integer, ServiceNode> virtualNodes = new TreeMap<>();

    public SortedMap<Integer, ServiceNode> getVirtualNodes() {
        return virtualNodes;
    }

    public ConsistentHashingVirtualNode(Collection<String> serviceIds) {
        this(serviceIds,VIRTUAL_NODES);
    }
    public ConsistentHashingVirtualNode(Collection<String> serviceIds, Integer virtualNodeCount) {
        // 遍历真实节点：
        for (String serviceId : serviceIds) {
            // 给每个真实节点添加虚拟节点
            addServiceNode(serviceId, virtualNodeCount);
        }
    }

    /**
     * 根据提供的用户id路由到对应的真实节点
     * @author Victor.Xiao
     * @since 2023-12-09 17:54
     * @param key key
     * @return com.ubxtech.core.hashring.ServiceNode
    */
    public ServiceNode getServiceNode(String key) {
        if (virtualNodes.isEmpty()) {
            log.warn("哈希环为空");
            return null;
        }
        int hashKey = Md5Util.hash(key);
        // 截取哈希环中比当前业务值哈希大的部分环 map
        SortedMap<Integer, ServiceNode> biggerTailMap = virtualNodes.tailMap(hashKey);
        // 获取路由到的虚拟节点的 hash
        Integer nodeHash;
        if (biggerTailMap.isEmpty()) {
            // 没有，回到整个哈希环的环首
            nodeHash = virtualNodes.firstKey();
        } else {
            // 存在，则为被截取后的 tailMap 的首个节点 key
            nodeHash = biggerTailMap.firstKey();
        }
        return virtualNodes.get(nodeHash);
    }

    /**
     * 新增节点
     * @author Victor.Xiao
     * @since 2023-12-09 17:54
     * @param serviceId 单个真实节点服务id
     */
    public boolean addServiceNode(String serviceId) {
        return addServiceNode(serviceId,VIRTUAL_NODES);
    }

    /**
     * 新增节点
     * @author Victor.Xiao
     * @since 2023-12-09 17:54
     * @param serviceId 单个真实节点服务id
     * @param virtualNodeCount 虚拟节点数量
    */
    public boolean addServiceNode(String serviceId, Integer virtualNodeCount) {
        // 先获取当前真实节点的虚拟节点数量
        Integer virtualNodeCountExistBefore = getVirtualNodeCountOf(serviceId);
        //不能超过最大值
        if (virtualNodeCountExistBefore >= virtualNodeCount) {
            if (log.isDebugEnabled()) {
                log.info("哈希环副本超过数量不再新增: {}, 虚拟副本节点数量 {}", serviceId, virtualNodeCountExistBefore);
            }
            return false;
        }
        log.info("【上线】哈希环新增一个真实节点: {}, 虚拟副本节点数量 {}", serviceId, virtualNodeCount);
        for (int i = 0; i < virtualNodeCount; i++) {
            ServiceNode serviceNode = new ServiceNode();
            serviceNode.setServiceId(serviceId);
            serviceNode.setReplicaIndex(++virtualNodeCountExistBefore);
            virtualNodes.put(Md5Util.hash(serviceNode.getVirtualServiceId()), serviceNode);
        }
        log.info("【上线】哈希环 最终结果: {}", virtualNodes.values());
        return true;
    }

    /**
     * 删除一个真实节点,需要把虚拟节点全部删除
     * @author Victor.Xiao
     * @since 2023-12-09 17:27
     * @param serviceId serviceId
    */
    public void removeServiceNode(String serviceId) {
        log.info("【下线】移除一个真实节点: {}", serviceId);
        // 实现注意遍历删除可能存在的并发修改异常
        Iterator<Integer> iterator = virtualNodes.keySet().iterator();
        while (iterator.hasNext()) {
            Integer nodeHashKey = iterator.next();
            ServiceNode serviceNode = virtualNodes.get(nodeHashKey);
            if (serviceNode.virtualNodeFlag(serviceId)) {
                log.info("【下线：遍历】删除哈希环对应的虚拟节点: {}", nodeHashKey);
                iterator.remove();
            }
        }
        log.info("【下线】哈希环 最终结果: {}", virtualNodes.values());
    }

    /**
     * 获取当前真实节点的虚拟节点的数量
     * @author Victor.Xiao
     * @since 2023-12-09 17:55
     * @param serviceId serviceId
     * @return java.lang.Integer
    */
    protected Integer getVirtualNodeCountOf(String serviceId) {
        int countVirtualNode = 0;
        for (ServiceNode serviceNode : virtualNodes.values()) {
            if (serviceNode.virtualNodeFlag(serviceId)) {
                countVirtualNode++;
            }
        }
        return countVirtualNode;
    }

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        ConsistentHashingVirtualNode consistentHashingVirtualNode =new ConsistentHashingVirtualNode(Arrays.asList("service_1","service_2","service_3","service_4"));
        List<String> ids = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            ids.add(UuidUtil.get32UUID());
        }
        for (String id : ids) {
            System.out.println("[" +id + "]的hash值为" + Md5Util.hash(id) + ", 被路由到结点[" + consistentHashingVirtualNode.getServiceNode(id) + "]");
        }
        System.out.println(System.currentTimeMillis()-start);
    }
}
